type MessageType = 'request' | 'response'

interface BaseMessage {
  type: MessageType
  id: string
}

interface RequestMessage extends BaseMessage {
  type: 'request'
  topic: string
  payload: unknown
}

interface ResponseMessage extends BaseMessage {
  type: 'response'
  data: unknown
}

type Message = RequestMessage | ResponseMessage

type MessageHandler = (payload: unknown) => Promise<unknown> | unknown
type MessageCallBack = (data: unknown) => void

export class TabCommuniction {
  private callbacks: Map<string, MessageCallBack>
  private handlers: Map<string, MessageHandler>
  private targetOrigin: string

  constructor(targetOrigin: string = '*') {
    this.callbacks = new Map()
    this.handlers = new Map()
    this.targetOrigin = targetOrigin

    window.addEventListener('message', async (event: MessageEvent<Message>) => {
      console.log('@@@ proxy via vintex: ', event.data)
      const message = event.data
      if (!message?.type) return

      if (message.type === 'request') {
        const handler = this.handlers.get(message.topic)
        if (handler) {
          const response = await handler(message.payload);
          (event.source as Window)?.postMessage(
            {
              type: 'response',
              id: message.id;
              data: response,
            },
            event.origin,
          );
        }    
      } else if (message.type === 'response') {
        const callbacks = this.callbacks.get(message.id);
        if(callbacks) {
            callbacks(message.data);
            this.callbacks.delete(message.id);
        }
      }
    })
  }

  async request<T = unknown>(targetWindow: Window, topic: string, payload?: unknown): Promise<T> {
    return new Promise(resolve => {
        const id = crypto.randomUUID();
        this.callbacks.set(id, resolve as MessageCallBack)

        targetWindow.postMessage(
          {
            type: 'request',
            id,
            topic,
            payload,
          } as RequestMessage,
          this.targetOrigin,
        );
    })
  }

  listen(topic: string, handler: MessageHandler): void {
    this.handlers.set(topic, handler);
  }
}

const comm = new TabCommuniction();
let vintexWindow: Window | null = null;
const WINDOW_NAME = 'vintexWindow';
const VINTEX_URL = 'http://localhost:8880';

if(!('__POWERED_BY_WUJIE__' in window)) {
    try {
        // Try to find existing window by name
        vintexWindow = window.open('', WINDOW_NAME);

        // Only open new window if it doesn't exist or was closed
        if (!vintexWindow || vintexWindow.closed) {
            vintexWindow = window.open(VINTEX_URL, WINDOW_NAME);
        }
        } catch (e) {
        // If we get a security error, assume we need to open a new window
        if (e instanceof DOMException) {
            vintexWindow = window.open(VINTEX_URL, WINDOW_NAME);
        } else {
            throw e;
        }
    }
}

export default {
    request: (topic: string, payload?: unknown) => 
        comm.request(vintexWindow as Window, topic, payload),
    listen: comm.listen,
}
